package com.geniusk.file.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.geniusk.file.config.MinioConfig;
import com.geniusk.file.core.File;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 文件上传工具类
 */
@Slf4j
@Component
public class MinioUtil {

    @Autowired
    private MinioConfig minioConfig;

    @Autowired
    private MinioClient minioClient;

    /**
     * 列出一个桶中的所有文件和目录
     */
    public List<File> selectFileList() {
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(minioConfig.getBucketName()).recursive(true).build());

        List<File> fileList = new ArrayList<>();
        for (Result<Item> r : results) {
            File file = new File();
            try {
                Item item = r.get();
                file.setFileName(FileUtil.getName(item.objectName()));
                file.setCreateTime(Date.from(item.lastModified().toInstant()));
                file.setFileSize(item.size());
                file.setObjectName(item.objectName());
                file.setFileUrl(getFileUrl(item.objectName()));
                fileList.add(file);
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        }
        return fileList;
    }

    /**
     * 预览图片
     * @param fileName 文件名称
     * @return 文件url
     */
    public String preview(String fileName){
        // 查看文件地址
        GetPresignedObjectUrlArgs build = GetPresignedObjectUrlArgs.builder()
                .bucket(minioConfig.getBucketName())
                .object(fileName)
                .method(Method.GET)
                .build();
        try {
            return minioClient.getPresignedObjectUrl(build);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return null;
    }

    /**
     * 上传文件
     * @param file 文件名称
     * @return 文件路径
     * @throws Exception 异常
     */
    public String uploadFile(MultipartFile file) throws Exception {
        String fileName = extractFilename(file.getOriginalFilename());
        InputStream inputStream = file.getInputStream();
        PutObjectArgs args = PutObjectArgs.builder()
                .bucket(minioConfig.getBucketName())
                .object(fileName)
                .stream(inputStream, file.getSize(), -1)
                .contentType(file.getContentType())
                .build();
        minioClient.putObject(args);
        return minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/" + fileName;
    }

    /**
     * 视频关键帧上传
     * @param bytes 视频关键帧
     * @return 关键帧图片预览路径
     */
    public String uploadVideoCover(byte[] bytes) {
        String extensionName = ".jpg";
        String originalFilename = UUID.fastUUID() + extensionName;
        String fileName = extractFilename(originalFilename);
        try {
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
            PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(minioConfig.getBucketName()).object(fileName)
                    .stream(byteArrayInputStream, bytes.length, -1).contentType("image/jpeg").build();
            //文件名称相同会覆盖
            minioClient.putObject(objectArgs);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/" + fileName;
    }

    /**
     * 获取待上传文件的预签名URL
     * @param objectName 文件名称
     * @return 上传地址
     */
    public String getUploadUrl(String objectName) {
        GetPresignedObjectUrlArgs getPresignedObjectUrlArgs = getGetPresignedObjectUrlArgs(Method.PUT,objectName);
        try {
            return minioClient.getPresignedObjectUrl(getPresignedObjectUrlArgs);
        }catch (Exception e){
            log.error(e.getMessage());
        }
        return null;
    }

    /**
     * 获取待下载文件的预签名URL
     * @param objectName 对象名称
     * @return 下载地址
     */
    public String getDownloadUrl(String objectName){
        GetPresignedObjectUrlArgs args = getGetPresignedObjectUrlArgs(Method.GET, objectName);
        try {
            return minioClient.getPresignedObjectUrl(args);
        }catch (Exception e){
            log.error(e.getMessage());
        }
        return null;
    }

    /**
     * 获取文件URL
     * @param objectName 对象名称
     * @return 文件URL
     */
    public String getFileUrl(String objectName){
        return minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/" + objectName;
    }

    /**
     * 删除文件
     * @param objectName 对象名称
     */
    public void deleteObject(String objectName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder()
                    .bucket(minioConfig.getBucketName())
                    .object(objectName)
                    .build());
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    /**
     * 获取预签名URL参数对象
     * @param objectName 对象名称
     * @return 参数对象
     */
    private GetPresignedObjectUrlArgs getGetPresignedObjectUrlArgs(Method method, String objectName){
        Map<String, String> reqParams = new HashMap<>();
        reqParams.put("response-content-type", "application/json");
        return GetPresignedObjectUrlArgs.builder()
                .method(method)
                .bucket(minioConfig.getBucketName())
                .object(objectName)
                .expiry(30, TimeUnit.MINUTES)
                .extraQueryParams(reqParams)
                .build();
    }

    /**
     * 编码文件名
     */
    public String extractFilename(String fileName) {
        if (StringUtils.isBlank(fileName)){
            throw new RuntimeException();
        }
        return StrUtil.format("{}/{}_{}.{}", DateUtil.format(new Date(),"yyyy-MM/dd"),
                FilenameUtils.getBaseName(fileName), IdUtil.getSnowflake().nextId(), FilenameUtils.getExtension(fileName));
    }

    public InputStream getFileInputStream(String objectName) throws Exception {
        return minioClient.getObject(
                GetObjectArgs.builder()
                        .bucket(minioConfig.getBucketName())
                        .object(objectName)
                        .build());
    }

    public long getSize(String objectName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        return  minioClient.statObject(
                StatObjectArgs.builder()
                        .bucket(minioConfig.getBucketName())
                        .object(objectName)
                        .build()).size();
    }
}